package com.ruoyi.web3jutil;

import org.web3j.crypto.Credentials;
import org.web3j.crypto.ECKeyPair;
import org.web3j.crypto.Keys;
import org.web3j.crypto.WalletUtils;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.response.EthGetBalance;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.Contract;
import org.web3j.tx.Transfer;
import org.web3j.tx.gas.DefaultGasProvider;
import org.web3j.utils.Convert;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutionException;

/**
 * Web3j工具类，用于以太坊区块链交互
 */
public class Web3jUtil {

    // 以太坊节点URL，可以根据实际情况修改
    private static final String ETHEREUM_NODE_URL = "http://localhost:8545";
    
    // 主地址池，存放创世账号
    private static final List<String> MAIN_ADDRESS_POOL = new ArrayList<>();
    private static final List<String> MAIN_PRIVATE_KEY_POOL = new ArrayList<>();
    
    // 初始化主地址池
    static {
        // 添加创世账号（这里是示例，实际使用时应该从配置文件读取或使用真实账号）
        MAIN_ADDRESS_POOL.add("0xb00c9B551773C0b3aaA2f4d690b1a8229a67aA84");
        MAIN_PRIVATE_KEY_POOL.add("0x35fdcde5d229b347d9b6f4ef57ebbcbf1857f7c73e20af2c30e7d0df191e329d");
        
        MAIN_ADDRESS_POOL.add("0x8f1491BE3B50a544fDc74f5a47F286fD34500996");
        MAIN_PRIVATE_KEY_POOL.add("0xd90aeafaf3aeab8029fdc2c5d1424049f6d75616cbbb99a6e05f918547a1818d");

        MAIN_ADDRESS_POOL.add("0xB94570B55C22b262081E1C75E1Af599419e5Ccf6");
        MAIN_PRIVATE_KEY_POOL.add("0xb13724b4c0a919be7dde4b95bf14750e600175d17be6decf17bf7e41d177465b");

    }
    
    /**
     * 获取Web3j连接
     * 
     * @return Web3j连接实例
     */
    public static Web3j getWeb3j() {
        return Web3j.build(new HttpService(ETHEREUM_NODE_URL));
    }
    
    /**
     * 从主地址池中随机弹出一个地址
     * 
     * @return 地址和私钥的数组，索引0为地址，索引1为私钥
     */
    public static String[] getRandomMainAddress() {
        if (MAIN_ADDRESS_POOL.isEmpty()) {
            return null;
        }
        
        Random random = new Random();
        int index = random.nextInt(MAIN_ADDRESS_POOL.size());
        
        String address = MAIN_ADDRESS_POOL.get(index);
        String privateKey = MAIN_PRIVATE_KEY_POOL.get(index);
        
        return new String[]{address, privateKey};
    }

    public static String[] getMainAddress(){
        if (MAIN_ADDRESS_POOL.isEmpty()) {
            return null;
        }

        String address = MAIN_ADDRESS_POOL.get(0);
        String privateKey = MAIN_PRIVATE_KEY_POOL.get(0);

        return new String[]{address, privateKey};
    }
    
    /**
     * 创建新的以太坊地址
     * 
     * @return 新创建的地址和私钥的数组，索引0为地址，索引1为私钥
     * @throws Exception 创建过程中可能出现的异常
     */
    public static String[] createNewAddress() throws Exception {
        // 创建新的密钥对
        ECKeyPair ecKeyPair = Keys.createEcKeyPair();
        String privateKey = ecKeyPair.getPrivateKey().toString(16);
        
        // 从私钥创建凭证
        Credentials credentials = Credentials.create(privateKey);
        String address = credentials.getAddress();
        
        // 从主地址池中随机选择一个地址向新地址转账2个ETH
        String[] mainAccount = getRandomMainAddress();
        if (mainAccount != null) {
            transferEther(mainAccount[1], address, new BigDecimal("2"));
        }
        
        return new String[]{address, privateKey};
    }
    
    /**
     * 转账函数
     * 
     * @param senderPrivateKey 发送者私钥
     * @param receiverAddress 接收者地址
     * @param amount 转账金额
     * @param currencyType 货币类型（目前仅支持ETH）
     * @return 交易收据
     * @throws Exception 转账过程中可能出现的异常
     */
    public static TransactionReceipt transfer(String senderPrivateKey, String receiverAddress, 
                                             BigDecimal amount, String currencyType) throws Exception {
        // 目前仅支持ETH转账
        if ("ETH".equalsIgnoreCase(currencyType)) {
            return transferEther(senderPrivateKey, receiverAddress, amount);
        } else {
            throw new IllegalArgumentException("Unsupported currency type: " + currencyType);
        }
    }
    
    /**
     * ETH转账
     * 
     * @param senderPrivateKey 发送者私钥
     * @param receiverAddress 接收者地址
     * @param etherAmount ETH金额
     * @return 交易收据
     * @throws Exception 转账过程中可能出现的异常
     */
    private static TransactionReceipt transferEther(String senderPrivateKey, String receiverAddress, 
                                                  BigDecimal etherAmount) throws Exception {
        Web3j web3j = getWeb3j();
        Credentials credentials = Credentials.create(senderPrivateKey);
        
        // 执行转账
        return Transfer.sendFunds(
                web3j, 
                credentials, 
                receiverAddress, 
                etherAmount, 
                Convert.Unit.ETHER
        ).send();
    }
    
    /**
     * 连接智能合约
     * 
     * @param web3j Web3j连接实例
     * @param credentials 操作者账号凭证
     * @param contractAddress 合约地址
     * @param contractClass 合约类Class对象
     * @param <T> 合约类型
     * @return 合约实例
     * @throws Exception 连接过程中可能出现的异常
     */
    public static <T extends Contract> T loadContract(Web3j web3j, Credentials credentials, 
                                                    String contractAddress, Class<T> contractClass) throws Exception {
        // 使用反射加载合约
        return contractClass.getDeclaredConstructor(
                String.class, Web3j.class, Credentials.class, DefaultGasProvider.class
        ).newInstance(contractAddress, web3j, credentials, new DefaultGasProvider());
    }
    
    /**
     * 获取账户ETH余额
     * 
     * @param address 账户地址
     * @return ETH余额
     * @throws IOException 获取过程中可能出现的异常
     */
    public static BigDecimal getEtherBalance(String address) throws IOException {
        Web3j web3j = getWeb3j();
        EthGetBalance ethGetBalance = web3j
                .ethGetBalance(address, DefaultBlockParameterName.LATEST)
                .send();
        
        BigInteger wei = ethGetBalance.getBalance();
        return Convert.fromWei(new BigDecimal(wei), Convert.Unit.ETHER);
    }
}